#include "rpmcpp.h"

#define UNUSE(PARAM) (void)PARAM

librpm::FieldContent *librpm::FieldContent::new_string_field(const char *name, const char *str, size_t len)
{
    FieldContent *self = new(std::nothrow) FieldContent();
    if (NULL == self)
        return NULL;

    self->type = EN_STR;
    self->name = name;

    self->content.str.ptr = new(std::nothrow) char[len];

    if (NULL == self->content.str.ptr)
    {
        delete self;
        return NULL;
    }
    memcpy(self->content.str.ptr, str, len);
    self->content.str.len = len;

    return self;
}

librpm::FieldContent *librpm::FieldContent::new_number_field(const char *name, int64_t number)
{
    FieldContent *self = new(std::nothrow) FieldContent();
    if (NULL == self)
        return NULL;

    self->type = EN_LONG;
    self->name = name;
    self->content.num = number;

    return self;
}

void librpm::FieldContent::del_field(librpm::FieldContent *self)
{
    if (NULL == self) {
        return ;
    }

    if (self->type == EN_DATE || self->type == EN_STR)
    {
        if (NULL != self->content.str.ptr)
        {
            delete [] self->content.str.ptr;
            self->content.str.ptr = NULL;
            self->content.str.len = 0;
        }
    }

    delete self;
}

librpm::FieldContent::FieldContent() {}

librpm::FieldContent::FieldContent(const librpm::FieldContent &) {}

librpm::FieldContent::~FieldContent() {

}

void librpm::CRpmQuery::librpm_configured(const char *dbpath)
{
    addMacro(NULL, "_dbpath", NULL, dbpath, 0);
    addMacro(NULL, "_dbapi", NULL, g_dbapi, 0);
}

librpm::CRpmQuery::CRpmQuery()
{
    m_ts = rpmtsCreate();
}

librpm::CRpmQuery::~CRpmQuery()
{
    rpmtsFree(m_ts);
    clean_result();
}

void librpm::CRpmQuery::query_all()
{
    int rpmrc = RPMRC_NOTFOUND;
    int tag = RPMDBI_PACKAGES;
    int gflags = RPMGI_NONE;
    int ftsOpts = 0;

    rpmgi gi = rpmgiNew(m_ts, (rpmTag)tag, NULL, 0);
    rpmRC rc = rpmgiSetArgs(gi, NULL, ftsOpts, (rpmgiFlags)gflags); /* rc unused */
    UNUSE(rc);

    if (gi != NULL && (rpmgiGetFlags(gi) & RPMGI_TSADD))	/* Load the ts with headers. */
    {
        while ((rpmrc = rpmgiNext(gi)) == RPMRC_OK) {};
    }
    if (rpmrc != RPMRC_NOTFOUND)
    {
        gi = rpmgiFree(gi);
        return;
    }

    // get header by gi
    while (rpmgiNext(gi) == RPMRC_OK)
    {
        Header h;

        h = rpmgiHeader(gi);
        if (NULL == h) continue;

        // traverse header fields, get content by header;
        SoftwarePackageInfos software;
        traverse_fields(h, software);
        m_software_list.push_back(software);
    }

    rpmtsEmpty(m_ts);
    gi = rpmgiFree(gi);
}

const librpm::TSoftwareList &librpm::CRpmQuery::get_results()
{
    return m_software_list;
}

void librpm::CRpmQuery::clean_result()
{
    for (TSoftwareList::iterator iter = m_software_list.begin(); iter != m_software_list.end(); ++iter)
    {
        SoftwarePackageInfos software = *iter;
        for (TFieldContents::iterator iter_field = software.m_fields.begin(); iter_field != software.m_fields.end(); ++iter_field)
        {
            FieldContent *field = *iter_field;

            if (NULL != field)
            {
                FieldContent::del_field(field);
            }
        }
    }
    m_software_list.clear();
}

int librpm::CRpmQuery::traverse_fields(Header h, librpm::SoftwarePackageInfos &software)
{
    const FieldType *fields = g_default_fields;
    size_t field_count = sizeof(g_default_fields) / sizeof(g_default_fields[0]);

    for (size_t var = 0; var < field_count; ++var)
    {
        int tag = rpmTagGetValue(fields[var].name.c_str());
        if (RPMTAG_NOT_FOUND == tag)
        {
            continue;
        }
        // cout << fields[var] << ".tag: " << tag << endl;
        rpmtd td = NULL;
        td = rpmtdNew();
        if (!headerGet(h, (rpmTag)tag, td, HEADERGET_EXT)) {
            rpmtdFree(td);
            continue;
        }

        if (EN_STR == fields[var].type)
        {
            const char *content = rpmtdGetString(td);
            software.m_fields.push_back(FieldContent::new_string_field(fields[var].name.c_str(), content, strlen(content) + 1));
            // 获取软件名称
            if (fields[var].name == "NAME")
            {
                software.m_software_name = content;
            }
        }
        else if (EN_LONG == fields[var].type)
        {
            software.m_fields.push_back(FieldContent::new_number_field(fields[var].name.c_str(), rpmtdGetNumber(td)));
        }
        else if (EN_DATE == fields[var].type)
        {
            struct tm tstruct;
            char buf[64];
            bzero(buf, sizeof(buf));
            time_t data_int = rpmtdGetNumber(td);
            localtime_r(&data_int, &tstruct);
            strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z", &tstruct);

            software.m_fields.push_back(FieldContent::new_string_field(fields[var].name.c_str(), buf, strlen(buf) + 1));
        }
        else
        {
            continue;
        }
    }
    return 0;
}
